
//--Yangxin--

`include "defines.v"

module alu(
	input wire  [15:0] alu_op    ,
	input wire  [63:0] alu_src1  ,
	input wire  [63:0] alu_src2  ,
	output wire [63:0] alu_result
	);

wire op_add  ;
wire op_sub  ;
wire op_and  ;
wire op_or   ;
wire op_xor  ;
wire op_sll  ;
wire op_srl  ;
wire op_sra  ;
wire op_slt  ;
wire op_sltu ;
wire op_lui  ;
wire op_sraiw;
wire op_sllw ;
wire op_srlw ;
wire op_csrc ;
wire op_csrw ;

//control code decomposition
assign op_add   = alu_op[ 0];
assign op_sub   = alu_op[ 1];
assign op_and   = alu_op[ 2];
assign op_or    = alu_op[ 3];
assign op_xor   = alu_op[ 4];
assign op_sll   = alu_op[ 5];
assign op_srl   = alu_op[ 6];
assign op_sra   = alu_op[ 7];
assign op_slt   = alu_op[ 8];
assign op_sltu  = alu_op[ 9];
assign op_lui   = alu_op[10];
assign op_sraiw = alu_op[11];
assign op_sllw  = alu_op[12];
assign op_srlw  = alu_op[13];
assign op_csrc  = alu_op[14];
assign op_csrw  = alu_op[15];

wire [ 63:0] add_sub_result ;
wire [ 63:0] and_result     ;
wire [ 63:0] sltu_result    ; 
wire [ 63:0] or_result      ;
wire [ 63:0] xor_result     ;
wire [ 63:0] sll_result     ;
wire [ 63:0] srl_result     ;
wire [ 63:0] sra_result     ;
wire [127:0] sr128_result   ;
wire [ 63:0] sr_result      ;
wire [127:0] sraiw128_result;
wire [ 63:0] sraiw_result   ;
 
wire [ 63:0] slt_result     ;
//wire [ 63:0] sltu_result    ;
wire [ 63:0] lui_result     ;

wire [ 63:0] sllw_result_temp;
wire [ 63:0] sllw_result    ;

wire [ 31:0] srlw_result_temp;
wire [ 63:0] srlw_result    ;
wire [ 63:0] csrc_result    ;
wire [ 63:0] csrw_result    ;

//64-bit adder
wire [ 63:0] adder_a        ;
wire [ 63:0] adder_b        ;
wire [ 63:0] adder_result   ;


wire         adder_cin      ;
wire         adder_cout     ;

assign adder_a   = alu_src1;
assign adder_b   = (op_sub | op_slt | op_sltu) ? ~alu_src2 : alu_src2;
assign adder_cin = (op_sub | op_slt | op_sltu) ? 1'b1 : 1'b0    ;
assign {adder_cout,adder_result} = adder_a + adder_b + {63'b0,adder_cin};



//ADD,SUB result
assign add_sub_result = adder_result;

//SLT result
assign slt_result[63:1]  = 63'b0;
assign slt_result[0]     = (alu_src1[63] & ~alu_src2[63])
					     | ((alu_src1[63] ^~ alu_src2[63]) & adder_result[63]);

//SLTU result
assign sltu_result[63:1] = 63'b0;
assign sltu_result[0]    = ~adder_cout; //why not = adder_cout;

//bitwise operation
assign and_result = alu_src1 & alu_src2;
assign or_result  = alu_src1 | alu_src2;
assign xor_result = alu_src1 ^ alu_src2;
assign lui_result = alu_src2;  //maybe wrong

//SLL result
assign sll_result = alu_src1 << alu_src2[5:0];
assign sllw_result_temp = alu_src1[31:0] << alu_src2[4:0];
assign sllw_result = {{32{sllw_result_temp[31]}},sllw_result_temp[31:0]};


//SRL result
assign sr128_result = {{64{op_sra & alu_src1[63]}},alu_src1[63:0]} >> alu_src2[5:0];
assign sr_result    = sr128_result[63:0];
assign sraiw128_result = {{96{alu_src1[31]}},alu_src1[31:0]} >> alu_src2[4:0];
assign sraiw_result = sraiw128_result[63:0];

assign srlw_result_temp = alu_src1[31:0] >> alu_src2[4:0];
//assign srlw_result = {32'd0,alu_src1[31:0]} >> alu_src2[4:0];
assign srlw_result = {{32{srlw_result_temp[31]}},srlw_result_temp};

//csrc result
assign csrc_result = (~alu_src1) & alu_src2;
assign csrw_result = alu_src1;

//final result mux
assign alu_result = ({64{op_add | op_sub}} & add_sub_result)
				  | ({64{op_and         }} & and_result    )
				  | ({64{op_or          }} & or_result     )
				  | ({64{op_xor         }} & xor_result    )
				  | ({64{op_sll         }} & sll_result    )
				  | ({64{op_srl | op_sra}} & sr_result     )
				  | ({64{op_slt         }} & slt_result    )
				  | ({64{op_sltu        }} & sltu_result   )
				  | ({64{op_lui			}} & lui_result    )
				  | ({64{op_sraiw       }} & sraiw_result  )
				  | ({64{op_sllw        }} & sllw_result   )
				  | ({64{op_srlw        }} & srlw_result   )
				  | ({64{op_csrc        }} & csrc_result   )
				  | ({64{op_csrw        }} & csrw_result   )
				  ;

endmodule
